﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../global/head.h"
#include "../macro/head.h"
#include "../tools/head.h"
#include "zlib_impl.h"

#if defined( XOS_IOS_SIMULATOR )
#include "../../../../xosbin/lib/ios_simulator/zlib/include/head.h"
#elif defined( XOS_IOS_STATIC )
#include "../../../../xosbin/lib/ios_static/zlib/include/head.h"
#elif defined( XOS_MAC )
#include "../../../../xosbin/lib/mac/zlib/include/head.h"
#elif defined( XOS_LINUX64 )
#include "../../../../xosbin/lib/linux64/zlib/include/head.h"
#elif defined( XOS_ANDROID )
#include "../../../../xosbin/lib/android/zlib/include/head.h"
#elif defined( XOS_LINUX )
#include "../../../../xosbin/lib/linux/zlib/include/head.h"
#elif defined( XOS_WIN32 )
#include "../../../../xosbin/lib/win32/zlib/include/head.h"
#elif defined( XOS_WIN64 )
#include "../../../../xosbin/lib/x64/zlib/include/head.h"
#endif


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 

namespace xos_compress
{
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static zlib_impl::POOL * pool_ptr = 0;
    
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//
	// 
	// 提供给7z的分配内存函数
	// 
	void *SzAlloc( voidpf opaque, uInt items, uInt size )
	{
		void * lpData = 0; 
		lpData = g_pSysData->m_pAlloc->my_malloc( items * size );
		return lpData;
	}

	void SzFree( voidpf opaque, voidpf address )
	{
		g_pSysData->m_pAlloc->my_free( address );
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//

    int zlib_impl::get_addr( POOL *** pppPool, LIST *** pppList )
    {
        int ret = 0;
        if( pppPool )
        {
            *pppPool = &pool_ptr;
        }
        if( pppList )
        {
            *pppList = 0;
        }
        return ret;
    }

	zlib_impl::zlib_impl()
	{
        z_stream * pStream = 0;
        xos_stl::init_obj( pStream );
        m_pStream = pStream;
        m_bInitStream = false;
        m_bCompress = false;
	}

	zlib_impl::~zlib_impl()
	{
        z_stream * pStream = ( z_stream* )m_pStream;
        xos_stl::term_obj( pStream );
        m_pStream = 0;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 

	int zlib_impl::init()
	{
		int ret = 0;
		return ret;
	}

	int zlib_impl::term()
	{
		int ret = 0;

        if( m_bInitStream )
        {
            z_stream * pStream = ( z_stream* )m_pStream;
            if( m_bCompress )
            {
                deflateEnd( pStream );
            }
            else
            {
                inflateEnd( pStream );
            }
        }

        m_bInitStream = false;
        m_bCompress = false;

		return ret;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 

	// 
	// 解压
	// 
	int zlib_impl::un_compress( char * lpszDest, int nDestLen, int * lpnDestLen, const char * lpszSrc, int nSrcLen )
	{
        int result = 1;
		int ret = 0;

        z_stream * pStream = ( z_stream* )m_pStream;
        z_stream & strm = *pStream;
        int nResultLen = 0;

		if( ( 0 == ret ) && !m_bInitStream )
		{
            strm.zalloc = SzAlloc;
            strm.zfree = SzFree;
            strm.opaque = Z_NULL;
            strm.avail_in = 0;
            strm.next_in = Z_NULL;
            int r = inflateInit( &strm );
            if( r == Z_OK )
            {
                m_bInitStream = true;
                m_bCompress = false;
            }
            else
            {
                result = -1;
                ret = 1;
            }
		}

        if( ( 0 == ret ) && ( nSrcLen > 0 ) )
        {
            strm.next_in = ( Bytef* )lpszSrc;
            strm.avail_in = nSrcLen;
        }

        if( 0 == ret )
        {
            strm.next_out = ( Bytef* )lpszDest;
            strm.avail_out = nDestLen;
        }

        if( 0 == ret )
        {
            int r = inflate( &strm, Z_NO_FLUSH );
            switch( r ) 
            {
            case Z_STREAM_END:
                {
                    result = 0;
                }
            case Z_NEED_DICT:
                {
                    r = Z_DATA_ERROR;     /* and fall through */
                }
            case Z_STREAM_ERROR:
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
                {
                    result = -1;
                }
                break;
            }
            if( result >= 0 )
            {
                nResultLen = nDestLen - strm.avail_out;
            }
        }

        if( ( result >= 0 ) && lpnDestLen )
        {
            *lpnDestLen = (int)nResultLen;
        }

		return result;
	}

	// 
	// 压缩, 0 : done, 1 : not done, -1 : failed
	// 
	int zlib_impl::compress( char * lpszDest, int nDestLen, int * lpnDestLen, const char * lpszSrc, int nSrcLen, bool bLast )
	{
        int result = 1;
        int ret = 0;

        z_stream * pStream = ( z_stream* )m_pStream;
        z_stream & strm = *pStream;
        int flush = bLast ? Z_FINISH : Z_NO_FLUSH;
        int nResultLen = 0;

        if( ( 0 == ret ) && !m_bInitStream )
        {
            strm.zalloc = SzAlloc;
            strm.zfree = SzFree;
            strm.opaque = Z_NULL;
            int r = deflateInit( &strm, Z_DEFAULT_COMPRESSION );
            if( r == Z_OK )
            {
                m_bInitStream = true;
                m_bCompress = true;
            }
            else
            {
                result = -1;
                ret = 1;
            }
        }

        if( ( 0 == ret ) && ( nSrcLen > 0 ) )
        {
            strm.next_in = ( Bytef* )lpszSrc;
            strm.avail_in = nSrcLen;
        }

        if( 0 == ret )
        {
            strm.next_out = ( Bytef* )lpszDest;
            strm.avail_out = nDestLen;
        }

        if( 0 == ret )
        {
            int r = deflate( &strm, flush );
            switch( r ) 
            {
            case Z_STREAM_ERROR:
                {
                    result = -1;
                }
                break;
            case Z_STREAM_END:
                {
                    result = 0;
                }
                break;
            }
            if( result >= 0 )
            {
                nResultLen = nDestLen - strm.avail_out;
            }
        }

        if( ( result >= 0 ) && lpnDestLen )
        {
            *lpnDestLen = (int)nResultLen;
        }

        return result;
	}

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int zlib_impl::un_compress( xos_box::i_buf * pDestBuf, xos_box::i_buf * pSrcBuf )
    {
        int ret = 0;
        int nDestBufSize = 0, nDestLen = 0, nDestOutLen = 0, nSrcLen = 0;
        const char * lpszSrc = pSrcBuf->get_data( 0, 0, &nSrcLen );
        char * lpszDest = pDestBuf->get_data( 0, 0, &nDestLen );
        nDestBufSize = xos_box::i_buf::BUF_SIZE - nDestLen;
        lpszDest += nDestLen;
        ret = un_compress( lpszDest, nDestBufSize, &nDestOutLen, lpszSrc, nSrcLen );
        if( 0 == ret )
        {
            nDestLen = nDestLen + nDestOutLen;
            pDestBuf->set_len( nDestLen );
        }
        return ret;
    }

	int zlib_impl::compress( xos_box::i_buf * pDestBuf, xos_box::i_buf * pSrcBuf, bool bLast )
	{
		int ret = 0;
		int nDestBufSize = 0, nDestLen = 0, nDestOutLen = 0, nSrcLen = 0;
        const char * lpszSrc = 0;
        if( pSrcBuf )
        {
            lpszSrc = pSrcBuf->get_data( 0, 0, &nSrcLen );
        }
		char * lpszDest = pDestBuf->get_data( 0, 0, &nDestLen );
		nDestBufSize = xos_box::i_buf::BUF_SIZE - nDestLen;
        lpszDest += nDestLen;
		ret = compress( lpszDest, nDestBufSize, &nDestOutLen, lpszSrc, nSrcLen, bLast );
		if( ( ret >= 0 ) && ( nDestOutLen > 0 ) )
		{
			nDestLen = nDestLen + nDestOutLen;
			pDestBuf->set_len( nDestLen );
		}
		return ret;
	}

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int zlib_impl::un_compress( xos_box::i_list * pDestList, xos_box::i_list * pSrcList )
    {
        int ret = 0;

        xos_box::i_buf * pDest = 0;
        xos_box::i_buf * pSrc = 0;

        while( ( 0 == ret ) && ( pSrc = ( xos_box::i_buf * )pSrcList->front( 0 ) ) )
        {
            pDest = ( xos_box::i_buf * )pDestList->back( 0 );
            if( !pDest || ( xos_box::i_buf::BUF_SIZE == pDest->get_len( 0 ) ) )
            {
                pDest = g_pSysData->m_pContainer->buf();
                pDestList->push_back( pDest );
            }
            ret = un_compress( pDest, pSrc );
            if( 0 == ret )
            {
                xos_stl::release_interface( pSrc );
                pSrcList->pop_front();
            }
        }

        return ret;
    }

    int zlib_impl::compress( xos_box::i_list * pDestList, xos_box::i_list * pSrcList )
    {
        int ret = 0;

        xos_box::i_buf * pDest = 0;
        xos_box::i_buf * pSrc = 0;

        while( 0 == ret )
        {
            pDest = ( xos_box::i_buf * )pDestList->back( 0 );
            pSrc = ( xos_box::i_buf * )pSrcList->front( 0 );
            if( !pDest || ( pDest->get_len( 0 ) == xos_box::i_buf::BUF_SIZE ) )
            {
                pDest = g_pSysData->m_pContainer->buf();
                pDestList->push_back( pDest );
            }
            xos_box::i_buf * pEnd = ( xos_box::i_buf * )pSrcList->back( 0 );
            int r = compress( pDest, pSrc, pSrc == pEnd );
            if( ( r >= 0 ) && pSrc )
            {
                xos_stl::release_interface( pSrc );
                pSrcList->pop_front();
            }
            if( r < 0 )
            {
                ret = 1;
                break;
            }
            if( 0 == r )
            {
                break;
            }
        }

        return ret;
    }

}
